feat(dream): --archive-dir flag persists CycleReport JSON + atomic latest.json symlink#1133
feat(dream): --archive-dir flag persists CycleReport JSON + atomic latest.json symlink#1133ChenyqThu wants to merge 1 commit into
Conversation
…on symlink Adds an --archive-dir <path> flag to `gbrain dream` that, when paired with --json, writes the CycleReport to <path>/<iso>.json (fs-safe colon-free timestamp) and atomically swaps a <path>/latest.json symlink to point at it. Motivation: the cron pattern of "spawn dream --json, capture stdout, parse, archive, swap a latest pointer" repeats across every downstream that wraps dream. Pushing the archive surface into core dream removes ~80 LoC from each wrapper while giving everyone the same atomic-swap contract (no dangling symlink visible to readers — rename is atomic on the same fs). Concrete fork case: jarvis-knowledge-os-v2 has a 205-line dream-wrap launchd worker whose primary value (after this PR lands) becomes the exit-code translation block (~30 lines); the entire archive + symlink surface (~80 lines) collapses into a single --archive-dir arg. Behavior: - --archive-dir without --json is a no-op + emits a stderr warning (printed human report has no JSON to archive) - Directory is auto-created (mkdirSync recursive) - ISO timestamp is colon-free (2026-05-17T23-45-12Z) so SMB / Windows shares accept the filenames - latest.json swap uses symlink-to-tmp + rename pattern (atomic on same fs); on platforms where symlink isn't supported the per-cycle file still lands and a stderr warning fires - Cycle status semantics unchanged (--archive-dir affects archive side-effect only, not exit code or report shape) Tests (29 pass, 0 fail in 19.46s): - test/dream-cli-flags.test.ts: 5 new static-introspection cases (flag declared, archive helper exists, no-op-without-json contract, isoStamp colon-free contract) - test/dream.test.ts: 5 new integration cases against real PGLite + real archive dir (per-cycle file + symlink shape, fs-safe filename, atomic swap on second run, auto-creates missing dir, no-op without --json) Typecheck: clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on 1) Filed 2026-05-17 with 29 pass / 0 fail test coverage. On merge, fork dream-wrap drops from 205 → ~30 LoC (exit-code translation only). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for this contribution — and apologies for the slow triage. We did a full pass over the entire PR backlog. gbrain has moved fast, and the maintainer's larger "cathedral" rewrites have superseded a big share of community PRs: the AI gateway + recipes + user_provided_models system replaced almost all individual provider PRs; #1805 fixed the whole Postgres module-singleton class; #1542 unified the type taxonomy; #1657 the retrieval path; #1802 the doctor; and so on. We're closing this one in that cleanup — either the fix already landed on master, it duplicates another PR or merged change, or it's outside the current merge bar. Where a closed PR carried a genuinely valuable idea, we've recorded it in docs/designs/COMMUNITY_IDEAS.md so nothing good is lost (a few may graduate into TODOs). Please don't read the close as a judgment of the work — thank you for contributing. If you believe the underlying issue is still live on the latest master, reopen with a quick note and we'll take another look. 🙏 |
Summary
Adds an
--archive-dir <path>flag togbrain dreamthat, when paired with--json, writes the CycleReport to<path>/<iso>.json(fs-safe colon-free timestamp) and atomically swaps a<path>/latest.jsonsymlink to point at it.Motivation
The cron pattern of "spawn
dream --json, capture stdout, parse JSON, archive to a per-cycle file, swap alatestpointer" repeats across every downstream that wrapsdream. Pushing the archive surface into core removes ~80 LoC from each wrapper while giving everyone the same atomic-swap contract (no dangling symlink ever visible to readers —renameis atomic on the same fs).Concrete downstream case: my fork
jarvis-knowledge-os-v2ships a 205-linedream-wraplaunchd worker whose primary value (after this PR lands) becomes the exit-code translation block (~30 lines). The entire archive + symlink surface (~80 lines) collapses into a single--archive-dirarg.Behavior
--archive-dirwithout--jsonis a no-op + emits a stderr warning (printed human report has no JSON to archive)mkdirSyncrecursive)2026-05-17T23-45-12Z) so SMB / Windows shares accept the filenameslatest.jsonswap uses symlink-to-tmp + rename pattern (atomic on the same fs); on platforms where symlink isn't supported the per-cycle file still lands and a stderr warning fires--archive-diraffects archive side-effect only, not exit code or report shape)Help text
Test plan
bun run typecheck— cleanbun test test/dream-cli-flags.test.ts test/dream.test.ts— 29 pass / 0 fail / 72 expect() calls / 19.46stest/dream-cli-flags.test.ts(flag declared, archive helper exists, no-op-without-json contract, isoStamp colon-free contract)test/dream.test.tsagainst real PGLite + real archive dir::, sortable, matches regex)--json+ stderr warning emittedDiff size
3 files / +205 / -3 — additive flag, no behavior change for existing callers.
Related downstream cleanup
On merge, my fork drops
skills/kos-jarvis/dream-wrap/run.tsfrom 205 LoC to ~30 (exit code translation only). Filed as PR-3 in my fork's TODO.Pattern matches the
garrytan/gbrainPR style of #1016 (additive AI recipe field) and the superseded #1017 (bootstrap probes) — fork-side experience codified into upstream so the wrapper layer thins.🤖 Generated with Claude Code